package com.lemon.boot.service.impl;

import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.RandomUtil;
import com.lemon.boot.common.consts.ApiConst;
import com.lemon.boot.common.consts.RedisKeyConst;
import com.lemon.boot.common.entity.LoginUserDetail;
import com.lemon.boot.common.enums.*;
import com.lemon.boot.common.exception.CustomException;
import com.lemon.boot.common.service.MailService;
import com.lemon.boot.common.service.RedisService;
import com.lemon.boot.common.service.SmsService;
import com.lemon.boot.common.thread.pool.ExecutorThreadPoolGroup;
import com.lemon.boot.common.utils.*;
import com.lemon.boot.common.web.Result;
import com.lemon.boot.dto.InviteDTO;
import com.lemon.boot.dto.UpdatePasswordDTO;
import com.lemon.boot.dto.UserDTO;
import com.lemon.boot.po.*;
import com.lemon.boot.service.*;
import com.lemon.boot.vo.RouteResourceVO;
import com.lemon.boot.vo.RouteVO;
import com.lemon.boot.vo.TreeSelectVO;
import com.lemon.boot.vo.WelcomeVO;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;

/**
 * @author 李猛
 * @datetime 2024/5/18 17:29
 * @description 类对象
 */
@Log4j2
@Service
public class WelcomeServiceImpl implements IWelcomeService {
    @Resource
    private IDeptService iDeptService;
    @Resource
    @Lazy
    private PasswordEncoder passwordEncoder;
    @Resource
    private RedisService redisService;
    @Resource
    private IUserService iUserService;
    @Resource
    private IRoleService iRoleService;
    @Resource
    private IResourceService iResourceService;
    @Resource
    private IMenuService iMenuService;
    @Resource
    private ITenantService iTenantService;
    @Resource
    private IDeptUserService iDeptUserService;
    @Resource
    private IUserRoleService iUserRoleService;
    @Resource
    private INoticeService iNoticeService;
    @Resource
    private MailService mailService;
    @Resource
    private SmsService smsService;
    @Resource
    private HttpServletResponse response;

    @Override
    public Map<String, Object> getApi() {
        //1.性别
        List<Dict> sex = Arrays.stream(SexEnum.values())
                .map(item -> Dict.create().set("key",item.getSex()).set("value",item.getInfo()))
                .collect(Collectors.toList());

        //2.状态
        List<Dict> status = Arrays.stream(StatusEnum.values())
                .map(item -> Dict.create().set("key",item.getStatus()).set("value",item.getInfo()))
                .collect(Collectors.toList());

        //3.当前用户的最高角色
        String role = iRoleService.getSuperKeyByCurrent();

        //5.封装返回值
        Map<String, Object> result = new HashMap<>();
        result.put("sex", sex);
        result.put("role", role);
        result.put("status", status);
        return result;
    }

    @Override
    public WelcomeVO getByCurrentUser() {
        //1.获取当前用户信息
        final LoginUserDetail currentUser = JwtUtil.getCurrentUser();

        //2.创建线程池
        Executor executor = ExecutorThreadPoolGroup.welcomeExecutorService;

        //3.异步查询当前用户信息
        CompletableFuture<Map<String, Object>> userMap = CompletableFuture.supplyAsync(() -> iUserService.getByCurrentId(currentUser.getUserId()), executor);

        //4.异步查询部门信息
        Map<String, Object> param = ApiConst.enableStatusAndUnableDelete();
        param.put("userId", currentUser.getUserId());
        param.put("tenantId", currentUser.getTenantId());
        CompletableFuture<List<String>> deptNameList = CompletableFuture.supplyAsync(() -> iDeptService.getDeptName(param), executor);

        //5.异步查询角色集合
        CompletableFuture<List<String>> roleNameList = CompletableFuture.supplyAsync(() -> {
            List<RolePO> roleList = iRoleService.query().select("name").in("id", currentUser.getRoleIdList()).list();
            return roleList.stream().map(RolePO::getName).collect(Collectors.toList());
        }, executor);

        //6.部门信息（部门：部门人数）
        CompletableFuture<Map<String, Integer>> deptMap = CompletableFuture.supplyAsync(() -> iDeptService.getDeptNameAndUserCount(param), executor);

        //7.封装视图对象
        WelcomeVO welcomeVO = new WelcomeVO();

        try {
            Map<String, Object> user = userMap.get();
            user.put("deptNameList", deptNameList.get());//部门名称
            user.put("roleNameList", roleNameList.get());//角色名称

            welcomeVO.setUser(user);
            welcomeVO.setDept(deptMap.get());
            return welcomeVO;
        } catch (InterruptedException | ExecutionException e) {
            log.error("UserServiceImpl.getByCurrentUser.message:{}", e.getMessage());
            throw new CustomException(e.getMessage());
        }
    }

    @Override
    public Result<String> getCurrentAvatar() {
        //1.当前用户
        LoginUserDetail currentUser = SecurityUtil.getLoginUserDetail();

        //2.查询
        UserPO userPO = iUserService.query().select("id", "avatar").eq("id", currentUser.getUserId()).last("limit 1").one();
        if (Objects.isNull(userPO)) {
            return Result.fail("用户信息错误！");
        }
        return Result.ok(userPO.getAvatar());
    }

    @Override
    public Result<String> getCurrentTenantName() {
        //1.当前用户
        LoginUserDetail currentUser = JwtUtil.getCurrentUser();

        //2.查询
        TenantPO tenantPO = iTenantService.query().select("id", "full_name").eq("id", currentUser.getTenantId()).last("limit 1").one();
        if (Objects.isNull(tenantPO)) {
            return Result.fail("公司信息错误！");
        }
        return Result.ok(tenantPO.getFullName());
    }

    @Override
    @Transactional
    public String updatePwd(Map<String, String> param) {
        //1.获取参数
        String oldPassword = param.get("oldPassword");
        String newPassword = param.get("newPassword");
        String confirmPassword = param.get("confirmPassword");
        if (!newPassword.trim().equals(confirmPassword.trim())) {
            return "新密码与确认密码不同，请修改！";
        }

        //2.获取当前用户信息
        final LoginUserDetail currentUser = SecurityUtil.getLoginUserDetail();

        //3.获取用户的密码
        UserPO userPO = iUserService.query().select("id", "password").eq("id", currentUser.getUserId()).one();
        if (!passwordEncoder.matches(oldPassword, userPO.getPassword())) {
            return "密码错误";
        }

        //4.修改密码
        iUserService.update().set("password", passwordEncoder.encode(newPassword)).eq("id", userPO.getId()).update();

        //5.清除redis
        redisService.del(currentUser.getUsername());
        return "修改成功！";
    }

    @Override
    public List<RouteResourceVO> getRouteResource() {
        return iResourceService.getByCurrent();
    }

    @Override
    public String inviteUser(InviteDTO inviteDTO) {
        //1.判断值是否正确
        if (CollectionUtils.isEmpty(inviteDTO.getDeptIdList())) {
            return "请选择部门！";
        }
        if (CollectionUtils.isEmpty(inviteDTO.getRoleIdList())) {
            return "请选择角色！";
        }
        if (!ValidateUtil.isEmail(inviteDTO.getEmail())) {
            return "邮箱格式错误！";
        }

        //2.设置邀请码
        String code = RandomUtil.randomString(8);
        inviteDTO.setCode(code);

        //3.设置租户ID
        inviteDTO.setTenantId(SecurityUtil.getLoginUserDetail().getTenantId());

        //4.定义内容
        String content = String.format("欢迎使用lemon-boot SAAS系统，您的邀请码是：%s，有效期%s分钟，请妥善保管！", code, inviteDTO.getVid());

        //5.定义邮件，并发送
        mailService.syncSend(inviteDTO.getEmail(), "邀请", content);

        //6.保存redis中
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_INVITE_USER, inviteDTO.getEmail());
        redisService.save(key, inviteDTO, inviteDTO.getVid(), TimeUnit.SECONDS);

        return "邀请成功！";
    }

    @Override
    public List<Dict> getInviteUserTime() {
        return Arrays.stream(InviteUserTime.values())
                .map(item -> Dict.create().set("value",item.getTimeByUnit()).set("desc",item.getDesc()))
                .collect(Collectors.toList());
    }

    @Override
    public List<RouteVO> getRoutes() {
        List<String> webFilePathList = iMenuService.getWebPathByCurrent();
        if (CollectionUtils.isEmpty(webFilePathList)) {
            return new ArrayList<>(0);
        }

        //3.处理视图对象并返回
        return webFilePathList.stream().map(path -> new RouteVO()
                .setComponent(path.endsWith(".vue") ? path : path + ".vue")
                .setParentPath(path.substring(0, path.lastIndexOf("/")))
                .setPath(path.substring(path.lastIndexOf("/") + 1, path.length() - 4).toLowerCase())
        ).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public String tenantRegister(Map<String, Object> map) {
        //1.租户信息并设置手机号，并添加
        TenantPO tenantPO = ConverterUtil.toBean(map, TenantPO.class);
        tenantPO.setPhone(Objects.toString(map.get("tel")));
        TenantPO tenant = iTenantService.register(tenantPO);

        //3.用户信息
        UserPO userPO = ConverterUtil.toBean(map, UserPO.class);
        userPO.setTenantId(tenant.getId());
        UserPO user = iUserService.register(userPO);

        //5.设置角色默认值，添加角色
        RolePO rolePO = new RolePO();
        rolePO.setTenantId(tenant.getId());
        rolePO.setName("管理员");
        rolePO.setRoleKey(RoleKeyEnum.ADMIN.getRoleKey());
        rolePO.setStatus(StatusEnum.ENABLE.getStatus());
        iRoleService.save(rolePO);

        //6.设置角色用户默认值，添加 sys_role_user
        UserRolePO userRolePO = new UserRolePO();
        userRolePO.setUserId(user.getId());
        userRolePO.setRoleId(rolePO.getId());
        iUserRoleService.save(userRolePO);

        return "注册成功，请耐心等待管理员分配权限！";
    }

    @Override
    public String userRegister(UserDTO userDTO) {
        //1.用户注册
        UserPO userPO = iUserService.register(userDTO);

        //2.获取邀请人信息
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_INVITE_USER, userDTO.getEmail());
        InviteDTO inviteDTO = redisService.get(key, InviteDTO.class);

        //3.添加角色
        List<UserRolePO> userRoleList = inviteDTO.getRoleIdList().stream().distinct().map(roleId -> new UserRolePO().setRoleId(roleId).setUserId(userPO.getId())).collect(Collectors.toList());
        iUserRoleService.saveBatch(userRoleList);

        //4.添加部门
        List<Long> deptIdList = iDeptService.getSuperId(inviteDTO.getTenantId(), inviteDTO.getDeptIdList());
        List<DeptUserPO> deptUserList = deptIdList.stream()
                .map(deptId -> new DeptUserPO()
                        .setDeptId(deptId)
                        .setUserId(userPO.getId()))
                .collect(Collectors.toList());
        iDeptUserService.saveBatch(deptUserList);

        //5.删除 redis里的信息
        redisService.del(userDTO.getCode());
        return "注册成功！";
    }

    @Override
    public void getCaptchaCode() {
        //1.生成验证码图片
        CodeUtil.Captcha captcha = CodeUtil.captchaCode(200, 100, 4, 100);

        //2.把验证码存入redis中
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_LOGIN_CAPTCHA_CODE, captcha.getCode());
        redisService.save(key, null, 1L, TimeUnit.MINUTES);

        //3.返回
        ResponseUtil.bytes(response, captcha.getBytes());
    }

    @Override
    public Dict sendByEmailOrPhone(Map<String, String> param) {
        //1.获取类型，email or message
        String active = param.get("active");

        //2.生成验证码
        String code = RandomUtil.randomNumbers(6);

        //3.定义有效期，单位分钟
        int validity = 5;

        //4.执行发送
        if ("email".equals(active)) {
            return sendEmail(param.get("email"), code, validity);
        } else if ("phone".equals(active)) {
            return sendMessage(param.get("phone"), code, validity);
        } else {
            return Dict.create().set("success", false).set("message", "号码输入有误！");
        }
    }

    /**
     * 发送邮件
     *
     * @param email
     * @param code
     * @param validity
     * @return
     */
    private Dict sendEmail(String email, String code, int validity) {
        //1.判断格式
        if (!ValidateUtil.isEmail(email)) {
            return Dict.create().set("success", false).set("message", "邮箱地址不正确！");
        }

        //2.验证输入的次数
        String keyCount = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_UPDATE_PASSWORD_COUNT, email);
        boolean locked = redisService.lockByTimes(keyCount, 3, 30, TimeUnit.MINUTES);
        if (!locked) {
            return Dict.create().set("success", false).set("message", "操作频繁，请30分钟后再试！");
        }

        //3.判断是否存在用户
        long count = iUserService.query().select("id").eq("email", email).eq("status", StatusEnum.ENABLE.getStatus()).count();
        if (count != 1L) {
            return Dict.create().set("success", false).set("message", "用户信息错误！");
        }

        //4.定义内容
        String content = String.format("欢迎使用lemon-boot SAAS系统，您的验证码是：%s，有效期%s分钟，请妥善保管！", code, validity);

        //5.定义邮件，并发送
        mailService.syncSend(email, "修改密码", content);

        //6.保存验证码到redis中
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_UPDATE_PASSWORD, email);
        redisService.save(key, code, validity, TimeUnit.MINUTES);
        return Dict.create().set("success", true).set("time", validity).set("message", "发送成功！");
    }

    /**
     * 发送短信
     *
     * @param phone
     * @param code
     * @param validity
     * @return
     */
    private Dict sendMessage(String phone, String code, int validity) {
        //1.验证手机号
        if (!ValidateUtil.isPhone(phone)) {
            return Dict.create().set("success", false).set("message", "手机号不正确！");
        }

        //2.验证输入的次数
        String keyCount = String.format("%s%s", RedisKeyConst.REDIS_KEY_MESSAGE_UPDATE_PASSWORD_COUNT, phone);
        boolean locked = redisService.lockByTimes(keyCount, 3, 30, TimeUnit.MINUTES);
        if (!locked) {
            return Dict.create().set("success", false).set("message", "操作频繁，请30分钟后再试！");
        }

        //3.判断是否存在用户
        long count = iUserService.query().select("id").eq("phone", phone).eq("status", StatusEnum.ENABLE.getStatus()).count();
        if (count != 1L) {
            return Dict.create().set("success", false).set("message", "用户信息错误！");
        }

        //4.发送短信
        boolean result = smsService.asyncSend(phone, Collections.singletonMap("code", code), SmsTemplateCodeEnum.FORGOT_PASSWORD);
        if (!result) {
            return Dict.create().set("success", false).set("message", "发送失败！");
        }

        //5.保存验证码到redis中
        String key = String.format("%s%s", RedisKeyConst.REDIS_KEY_MESSAGE_UPDATE_PASSWORD, phone);
        redisService.save(key, code, validity, TimeUnit.MINUTES);
        return Dict.create().set("success", true).set("time", validity).set("message", "发送成功！");
    }

    @Override
    public Result<String> updatePwdByEmailOrPhone(UpdatePasswordDTO updatePasswordDTO) {
        //1.验证密码
        if (!updatePasswordDTO.getNewPassword().equals(updatePasswordDTO.getConfirmPassword())) {
            return Result.fail("两次输入密码不一致！");
        }
        if (!StringUtils.hasText(updatePasswordDTO.getNewPassword()) || updatePasswordDTO.getNewPassword().length() < 5) {
            return Result.fail("密码长度不能小于5位！");
        }

        //2.缓存 key
        String key;

        //3.验证参数
        if ("email".equals(updatePasswordDTO.getActive()) && ValidateUtil.isEmail(updatePasswordDTO.getEmail())) {
            key = String.format("%s%s", RedisKeyConst.REDIS_KEY_EMAIL_UPDATE_PASSWORD, updatePasswordDTO.getEmail());
        } else if ("phone".equals(updatePasswordDTO.getActive()) && ValidateUtil.isPhone(updatePasswordDTO.getPhone())) {
            key = String.format("%s%s", RedisKeyConst.REDIS_KEY_MESSAGE_UPDATE_PASSWORD, updatePasswordDTO.getPhone());
        } else {
            return Result.fail("号码输入有误！");
        }

        //4.验证验证码
        boolean hasKey = redisService.has(key);
        if (!hasKey) {
            return Result.fail("号码输入有误！");
        } else {
            String cacheCode = redisService.get(key, String.class);
            if (!StringUtils.hasText(cacheCode) || !cacheCode.equals(updatePasswordDTO.getCode())) {
                return Result.fail("验证码输入有误！");
            }
        }

        //5.修改密码
        iUserService.update()
                .set("password", passwordEncoder.encode(updatePasswordDTO.getNewPassword()))
                .eq("email".equals(updatePasswordDTO.getActive()), "email", updatePasswordDTO.getEmail())
                .eq("phone".equals(updatePasswordDTO.getActive()), "phone", updatePasswordDTO.getPhone())
                .eq("status", StatusEnum.ENABLE.getStatus())
                .update();

        //6.清除redis
        redisService.del(key);

        return Result.ok("密码修改成功！");
    }

    @Override
    public String addNotice(NoticePO noticePO) {
        return iNoticeService.add(noticePO);
    }

    @Override
    public NoticePO getNoticeById(Long id) {
        return iNoticeService.get(id);
    }

    @Override
    public List<NoticePO> getNoticeList(Map<String, Object> map) {
        return iNoticeService.listByMap(map);
    }

    @Override
    public Map<String, Object> getByCurrent() {
        //1.当前用户信息
        final LoginUserDetail currentUser = JwtUtil.getCurrentUser();

        //2.创建线程池
        Executor executor = ExecutorThreadPoolGroup.welcomeExecutorService;

        //3.异步查询当前用户信息
        CompletableFuture<UserPO> user = CompletableFuture.supplyAsync(() -> iUserService.get(currentUser.getUserId()), executor);

        //4.角色信息
        CompletableFuture<List<RolePO>> roleList = CompletableFuture.supplyAsync(() -> iRoleService.getByCurrentTenantId(currentUser.getTenantId()), executor);

        //5.部门信息
        CompletableFuture<List<TreeSelectVO>> deptList = CompletableFuture.supplyAsync(() -> iDeptService.getByTreeSelectByTenantId(currentUser.getTenantId()), executor);

        try {
            Map<String, Object> result = new HashMap<>(3);
            result.put("user", user.get());//用户信息
            result.put("roleList", roleList.get());//角色信息
            result.put("deptList", deptList.get());//部门信息
            return result;
        } catch (InterruptedException | ExecutionException e) {
            log.error("UserServiceImpl.getByCurrent.message:{}", e.getMessage());
            throw new CustomException(e.getMessage());
        }
    }
}
